Show Sidebar Hide Sidebar

geom_violin in ggplot2

How to make a density map using geom_violin. Includes explanations on flipping axes and facetting.

Basic violin plot

A basic violin plot showing how Democratic vote share in the 2018 elections to the US House of Representatives varied by level of density. A horizontal bar is added, to divide candidates who lost from those who won.

Source: Dave Wassermann and Ally Flinn for the election results and CityLab for its Congressional Density Index. Regional classifications are according to the Census Bureau.

library(plotly)
district_density <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/district_density.csv", stringsAsFactors = FALSE)
district_density$cluster <- factor(district_density$cluster, levels=c("Pure urban", "Urban-suburban mix", "Dense suburban", "Sparse suburban", "Rural-suburban mix", "Pure rural"))
district_density$region <- factor(district_density$region, levels=c("West", "South", "Midwest", "Northeast"))

p <- ggplot(district_density,aes(x=cluster, y=dem_margin, fill=cluster)) +
  geom_violin(colour=NA) +
  geom_hline(yintercept=0, alpha=0.5) +
  labs(title = "Democratic performance in the 2018 House elections, by region and density",
       x = "Density Index\nfrom CityLab",
       y = "Margin of Victory/Defeat")

ggplotly(p)

Flipping the Axes

With geom_violin(), the y-axis must always be the continuous variable, and the x-axis the categorical variable. To create horizontal violin graphs, keep the x- and y-variables as is and add coord_flip().

library(plotly)
district_density <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/district_density.csv", stringsAsFactors = FALSE)
district_density$cluster <- factor(district_density$cluster, levels=c("Pure urban", "Urban-suburban mix", "Dense suburban", "Sparse suburban", "Rural-suburban mix", "Pure rural"))
district_density$region <- factor(district_density$region, levels=c("West", "South", "Midwest", "Northeast"))

p <- ggplot(district_density,aes(x=cluster, y=dem_margin, fill=cluster)) +
  geom_violin(colour=NA) +
  geom_hline(yintercept=0, alpha=0.5) +
  labs(title = "Democratic performance in the 2018 House elections, by region and density",
       x = "Density Index\nfrom CityLab",
       y = "Margin of Victory/Defeat") +
  coord_flip()

ggplotly(p)

Add facetting

Including facetting by region.

library(plotly)
district_density <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/district_density.csv", stringsAsFactors = FALSE)
district_density$cluster <- factor(district_density$cluster, levels=c("Pure urban", "Urban-suburban mix", "Dense suburban", "Sparse suburban", "Rural-suburban mix", "Pure rural"))
district_density$region <- factor(district_density$region, levels=c("West", "South", "Midwest", "Northeast"))

p <- ggplot(district_density,aes(x=cluster, y=dem_margin, fill=cluster)) +
  geom_violin(colour=NA) +
  geom_hline(yintercept=0, alpha=0.5) +
  facet_wrap(~region) +
  labs(title = "Democratic performance in the 2018 House elections, by region and density",
       x = "Density Index\nfrom CityLab",
       y = "Margin of Victory/Defeat") +
  coord_flip()

ggplotly(p)

Customized Appearance

Add colour to the facet titles, centre-align the title, rotate the y-axis title, change the font, and get rid of the unnecessary legend. Note that coord_flip() flips the axes for the variables and the titles, but does not flip theme() elements.

library(plotly)
district_density <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/district_density.csv", stringsAsFactors = FALSE)
district_density$cluster <- factor(district_density$cluster, levels=c("Pure urban", "Urban-suburban mix", "Dense suburban", "Sparse suburban", "Rural-suburban mix", "Pure rural"))
district_density$region <- factor(district_density$region, levels=c("West", "South", "Midwest", "Northeast"))

p <- ggplot(district_density,aes(x=cluster, y=dem_margin, fill=cluster)) +
  geom_violin(colour=NA) +
  geom_hline(yintercept=0, alpha=0.5) +
  facet_wrap(~region) +
  labs(title = "Democratic performance in the 2018 House elections, by region and density",
       x = "Density Index\nfrom CityLab",
       y = "Margin of Victory/Defeat") +
  coord_flip() +
  theme(axis.title.y = element_text(angle = 0, vjust=0.5),
        plot.title = element_text(hjust = 0.5),
        strip.background = element_rect(fill="lightblue"),
        text = element_text(family = 'Fira Sans'),
        legend.position = "none")

ggplotly(p)

Rotated Axis Text

Rotated the x-axis text 45 degrees, and used facet_grid to create a 4x1 facet (compared to facet_wrap, which defaults to 2x2).

library(plotly)
district_density <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/district_density.csv", stringsAsFactors = FALSE)
district_density$cluster <- factor(district_density$cluster, levels=c("Pure urban", "Urban-suburban mix", "Dense suburban", "Sparse suburban", "Rural-suburban mix", "Pure rural"))
district_density$region <- factor(district_density$region, levels=c("West", "South", "Midwest", "Northeast"))

p <- ggplot(district_density,aes(x=cluster, y=dem_margin, fill=cluster)) +
  geom_violin(colour=NA) +
  geom_hline(yintercept=0, alpha=0.5) +
  facet_grid(.~region) +
  labs(title = "Democratic performance in the 2018 House elections, by region and density",
       x = "Density Index\nfrom CityLab",
       y = "Margin of Victory/Defeat") +
  theme(axis.text.x = element_text(angle = -45),
        plot.title = element_text(hjust = 0.5),
        strip.background = element_rect(fill="lightblue"),
        text = element_text(family = 'Fira Sans'),
        legend.position = "none")

ggplotly(p)